package main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"
)

const GBBytes int64 = 1073741824

type Logger struct {
	*log.Logger
	name   string
	dir    string
	pure   bool
	file   *os.File
	writer *bufio.Writer
	mu     *sync.Mutex
}

// 文件名全路径
func NewLogger(name string, pure bool) *Logger {

	pwd, _ := filepath.Abs("")
	logDir := pwd + "/logs"
	exist, _ := PathExists(logDir)
	if !exist {
		log.Println("创建日志目录: ", logDir)
		os.Mkdir(logDir, os.ModePerm)
	}

	l := &Logger{
		name: name,
		dir:  logDir,
		pure: pure,
		mu:   new(sync.Mutex),
	}
	l.open()
	return l
}

func (l *Logger) Println(v ...interface{}) {
	l.check()
	l.Logger.Output(2, fmt.Sprintln(v...))
}

func (l *Logger) Printf(format string, v ...interface{}) {
	l.check()
	content := fmt.Sprintf(format, v...)
	l.Logger.Output(2, fmt.Sprintf("%s\n", content))
}

func (l *Logger) tooBig() bool {
	fileInfo, err := l.file.Stat()
	if err != nil {
		log.Println("日志文件状态错误: ", err.Error())
		return true
	}
	return fileInfo.Size() >= GBBytes
}

func (l *Logger) check() {

	if l.tooBig() {

		l.mu.Lock()
		defer l.mu.Unlock()

		if !l.tooBig() {
			return
		}

		l.reopen()
	}

}

func (l *Logger) Flush() {
	l.writer.Flush()
}

func (l *Logger) Close() {
	l.Flush()
	l.file.Close()
	l.writer = nil
}

func (l *Logger) reopen() {
	l.Close()
	l.open()
}

func (l *Logger) open() {

	timeStamp := time.Now().Format("20060102150405")
	finalFileName := fmt.Sprintf("%s/%s_%s.log", l.dir, l.name, timeStamp)

	f, openErr := os.OpenFile(finalFileName, os.O_APPEND|os.O_RDWR|os.O_CREATE, 0666)
	if openErr != nil {
		panic(openErr)
	}

	l.file = f

	writer := bufio.NewWriterSize(f, 1)

	var originLogger *log.Logger
	if l.pure {
		originLogger = log.New(writer, "", 0)
	} else {
		originLogger = log.New(writer, "", log.Ldate|log.Lmicroseconds)
	}

	l.Logger = originLogger
	l.writer = writer
	l.file = f

}

func PathExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}
